package doh.service;

import javax.ejb.*;
import javax.persistence.*;
import javax.persistence.criteria.*;
import javax.annotation.*;
import java.util.*;
import doh.entity.*;
import doh.util.*;
import doh.logic.*;

@Stateless
public class RequestBean implements Request {
    @PersistenceContext
    private EntityManager em;

    private CriteriaBuilder cb;
    @PostConstruct
    private void init() {
        cb = em.getCriteriaBuilder();
    }
    public void addPlayer(
            String id,
            String password,
            int rating,
            int games,
            int wins,
            double wratio,
            double oratio,
            int rocks,
            int papers,
            int scissors) {
        Player newPlayer = new Player(id, password, rating, games, wins, wratio, oratio, rocks, papers, scissors);
        em.persist(newPlayer);
    }

    public void dropPlayer(String id) {
        Player deadPlayer = em.find(Player.class, id);
        em.remove(deadPlayer);
    }

    public List<Player> getPlayers() {
        List<Player> allPlayers = null;
        try {
            CriteriaQuery<Player> cq = cb.createQuery(Player.class);

            if (cq != null) {
                Root<Player> player = cq.from(Player.class);
                cq.select(player);
                TypedQuery<Player> q = em.createQuery(cq);
                allPlayers = q.getResultList();
            }
        } catch (Exception ex) {
            throw new EJBException(ex);
        }
        return (allPlayers);
    }

    public int getRating(String id) {
        System.out.println("Inside getRating and the id is: " + id);
        Player myPlayer = new Player();
        try
            {
                myPlayer = em.find(Player.class, id);
            }
            catch (Exception ex)
            {
                System.err.println("Caught an exception:");
                ex.printStackTrace();
            }
        
        return myPlayer.get_rating();
    }

    public void adjust_stats(String p1, String p2, boolean winner, int adjustment, GameSessionDetails gsd) {
        Player player1 = new Player();
        try
            {
                player1 = em.find(Player.class, p1);
            }
            catch (Exception ex)
            {
                System.err.println("Caught an exception:");
                ex.printStackTrace();
            }

        Player player2 = new Player();
        try
            {
                player2 = em.find(Player.class, p2);
            }
            catch (Exception ex)
            {
                System.err.println("Caught an exception:");
                ex.printStackTrace();
            }
  
        if (winner) {
            player1.set_wins(player1.get_wins()+1);
            player1.set_rating(player1.get_rating()+adjustment);
            player2.set_rating(player2.get_rating()-adjustment);
        } else {
            player1.set_rating(player1.get_rating()-adjustment);
            player2.set_rating(player2.get_rating()+adjustment);
            player2.set_wins(player2.get_wins()+1);
        }
        player1.set_games(player1.get_games()+1);
        player1.set_rocks(player1.get_rocks()+gsd.getPlayer1().getRock());
        player1.set_papers(player1.get_papers()+gsd.getPlayer1().getPaper());
        player1.set_scissors(player1.get_scissors()+gsd.getPlayer1().getScissor());
        player2.set_games(player2.get_games()+1);
        player2.set_rocks(player2.get_rocks()+gsd.getPlayer2().getRock());
        player2.set_papers(player2.get_papers()+gsd.getPlayer2().getPaper());
        player2.set_scissors(player2.get_scissors()+gsd.getPlayer2().getScissor());
        player1.set_wratio((double)player1.get_wins()/player1.get_games());
        player2.set_wratio((double)player2.get_wins()/player2.get_games());
        player1.set_oratio((((player1.get_games()-1)*player1.get_oratio())+player2.get_wratio())/player1.get_games());
        player2.set_oratio((((player2.get_games()-1)*player2.get_oratio())+player1.get_wratio())/player2.get_games());
    }

    public int calc_ELO_adjustment(int r1, int r2, boolean winner) {
        int max_adj = 32;
        double multiplier = 1.0;
        if (winner)
            multiplier = (1/(1+Math.pow(10.0,(r1-r2)/400.0)));
        else
            multiplier = (1/(1+Math.pow(10.0,(r2-r1)/400.0)));
        int adjustment = (int)Math.round(max_adj * multiplier);
        return adjustment;
    }

    public void addGame(GameSessionDetails gsd) {
        String p1 = gsd.getPlayer1().getName();
        String p2 = gsd.getPlayer2().getName();
        int r1 = getRating(p1);
        int r2 = getRating(p2);
        boolean winner = gsd.getWinner();
        int adjustment = calc_ELO_adjustment(r1, r2, winner);
        adjust_stats(p1, p2, winner, adjustment, gsd);
        Game completedGame = new Game(p1, p2, r1, r2, winner, adjustment);
        em.persist(completedGame);
    }

    public List<Game> getGames() {
        List<Game> allGames = null;
        try {
            CriteriaQuery<Game> cq = cb.createQuery(Game.class);

            if (cq != null) {
                Root<Game> game = cq.from(Game.class);
                cq.select(game);
                TypedQuery<Game> q = em.createQuery(cq);
                allGames = q.getResultList();
            }
        } catch (Exception ex) {
            throw new EJBException(ex);
        }
        return (allGames);
    }

    public List<Game> getHistory(String player) {
        List<Game> allGames = getGames();
        List<Game> playerHistory = new ArrayList();
        Iterator itr = allGames.iterator();
        while (itr.hasNext()) {
            Game aGame = (Game)itr.next();
            if (player.equals(aGame.get_p1()) || player.equals(aGame.get_p2())) {
                playerHistory.add(aGame);
            }
        }
        return playerHistory;
    }

    public List<LadderEntry> getLadder() {
        List<LadderEntry> rpsLadder = new ArrayList();
        List<Player> allPlayers = getPlayers();
        Iterator itr = allPlayers.iterator();
        while (itr.hasNext()) {
            Player aPlayer = (Player)itr.next();
            rpsLadder.add(new LadderEntry(aPlayer.get_id(), aPlayer.get_rating()));
        }
        Collections.sort(rpsLadder);
        return rpsLadder;
    }

    //GAME LOGIC METHODS

    static boolean playerAwaiting = false;
    static String awaitingPlayer;

    static Vector games = new Vector();
    static int gameID = -1;

    public int matchPlayer(String player)
    {

        if(playerAwaiting == false)
        {
            playerAwaiting = true;
            awaitingPlayer = player;
            while(playerAwaiting);
            return gameID;
        }

        else
        {
            System.out.println("\n\nPlayer1: " + awaitingPlayer + "\nPlayer2: " + player + "\n\n");
            games.add( new GameSession(awaitingPlayer, player) );
            gameID++;
            playerAwaiting = false;
            return gameID;
        }

    }

    public Vector throwObject(String player, int obj, int gameID)
    {
        Vector gameSD;
        gameSD = ((GameSession)(games.get(gameID))).throwObject(player, obj);

        return gameSD;
    }
}
